#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

/* gcc -g -Wall  code.c -o shell `pkg-config --libs --cflags glib-2.0`*/
int attente_fils(int pid) {
  // renvoie 1 si le fils s'est terminé
  //         0 sinon
  int status;
  pid_t res;

  res = waitpid(pid, &status, 0);

  if (res < 0) {
    if (errno == 10) {// erreur no child process
      // signifie que le le fils a déjà été traité
      // on ignore
      return 1;
    }

    fprintf(stderr, "erreur avec le waitpid %s\n", strerror(errno));
    exit(1);
  }

  if (WIFEXITED(status)) {
    fprintf(stdout, "la commande s'est terminée en renvoyant %d\n", WEXITSTATUS(status));
    return 1;
  }
 
  if (WIFSIGNALED(status)) {
    fprintf(stdout, "la commande s'est terminée suite au signal %d\n", WTERMSIG(status));
    return 1;
  }
 
  if (WIFSTOPPED(status)) {
    fprintf(stdout, "la commande à été stoppée par le signal %d\n", WSTOPSIG(status));
    return 0;
  }
 
  if (WIFCONTINUED(status)) {
    fprintf(stdout, "la commande à été relancée\n");
    return 0;
  }

  fprintf(stdout, "ceci ne devrait pas arriver\n");
  exit(1);
}


void     traitement_signal_fils (int sig, siginfo_t *info, void *ingore) {
  int res;


  //  fprintf(stdout, "coucou\n");
  res = attente_fils(info->si_pid);
  if (res != 1) {
    // Ce n'est pas la fin d'un fils cela ne devrait pas arriver
    fprintf(stderr, "Ceci ne devrait pas arriver\n");
    exit(1);
  }

}

int executevp(const char *file, char *const argv[]) {
  char *const *pt;
  fprintf(stdout, "Le shell doit éxécuter la commande %s avec les arguments :#", file);
   
  pt = argv;
  while (*pt != NULL) {
    fprintf(stdout, "%s## ",*pt);
    pt++;
  }
  fprintf(stdout, "\n");
  return 0;
}


int main(int argc, char *argv[]) {
  //

  struct sigaction sa;
  sigaction(SIGCHLD, NULL, &sa);
  sa.sa_flags = SA_SIGINFO|SA_NOCLDSTOP|SA_RESTART;
  sa.sa_sigaction = traitement_signal_fils;
 
  sigaction(SIGCHLD, &sa, NULL);

  while (1) {
    int attend;
    char **tab_com_pipe = NULL;
    char **arg_com1 = NULL;
    char **arg_com2 = NULL;
    char *text_com = NULL;
    size_t taille_com = 0;
    ssize_t res = -1;
    pid_t pid_com1 = -1;
    pid_t pid_com2 = -1;

    //    text_com = readline("entrez une commande>");
    fprintf(stdout, "entrez une commande>");
    fflush(stdout);

    res = getline(&text_com, &taille_com, stdin);
    if (res < 0) {
      perror("problème de lecture sur la ligne de commande");
      exit(1);
    }
    if (text_com != NULL) {

      g_strdelimit(text_com, "\t\n", ' ');
      g_strstrip(text_com);

      fprintf(stderr, "lecture de la commande \"%s\"\n", text_com);
      if (strcmp(text_com, "fin")==0) {
        // Si la commande est "fin" on termine le shell
        fprintf(stderr, "bye\n");
        free(text_com);
        break;
      }

      if (strlen(text_com) == 0) {
        // si la chaine est vide.
        fprintf(stderr, "La comande est vide\n");
        free(text_com);
        continue;
      }
     
      if (text_com[strlen(text_com)-1] == '&') {
        // il y a un & à la fin
        // on doit faire une commande en tâche de fond.
        attend = false;
        text_com[strlen(text_com)-1] = '\0';
        g_strstrip(text_com);
      } else {
        // Le shell devra attendre la fin de la commande
        attend = true;
      }
     
      tab_com_pipe = g_strsplit(text_com, "|", -1);
     
     
      if (g_strv_length(tab_com_pipe) == 1) {
        pid_t pid_com1;
 
        // il n'y a pas de pipe, une seule commande à traiter

        arg_com1 = g_strsplit_set(tab_com_pipe[0], "\t ", -1);

        // ######################################
        // Code à modifier ici
        // ######################################
        // vous devez appeler la fonction execvp avec les arguments identiques à executevp
        // de manière à ce que le shell execute la commande en premier plan si attend = true
        // en tâche de fond sinon

        pid_com1 = fork();
        if (pid_com1 < 0) {
          perror("Il y a eu un problème durant le fork()");
          exit(1);
        }
        if (pid_com1 == 0) {
          fprintf(stderr, "lancement de %s\n", text_com);
          execvp(arg_com1[0], arg_com1);
          fprintf(stderr, "erreur avec la commande %s car : %s\n", text_com, strerror(errno));
          exit(2);
        } else {

          if (attend) { // attente blocante si on en a besoin
            attente_fils(pid_com1);
          }
        }
       

      }
      else if (g_strv_length(tab_com_pipe) == 2) {
        int pipefd[2];

        // il y a un seul pipe
        g_strstrip(tab_com_pipe[0]);
        g_strstrip(tab_com_pipe[1]);
       
        arg_com1 = g_strsplit_set(tab_com_pipe[0], "\t ", -1);
        arg_com2 = g_strsplit_set(tab_com_pipe[1], "\t ", -1);
        // ######################################
        // Code à modifier ici
        // ######################################
        // vous devez appeler la fonction execvp avec les arguments identiques à executevp
        // de manière à ce que le shell execute les deux  commandes avec un pipe entre elle

        if (pipe(pipefd) < 0)
          printf("pipe error\n");

        pid_com1 = fork();
        if (pid_com1 < 0) {
          perror("Il y a eu un problème durant le fork()");
          exit(1);
        }
        if (pid_com1 == 0) {
          //premier fils qui ecrit sur le pipe
          close(pipefd[0]);
          if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
            fprintf(stderr, "probleme avec la redirection en sortie : %s", strerror(errno));
          }
          fprintf(stderr, "lancement de %s\n", arg_com1[0]);
          execvp(arg_com1[0], arg_com1);
          //      write(pipefd[0], "coucou\n", 7);
          fprintf(stderr, "erreur avec la commande %s car : %s\n", arg_com1[0], strerror(errno));
          close(pipefd[1]);
          exit(2);
        } else{// c'est le pere qui doit en lancer un autre encore
          close(pipefd[1]);
          pid_com2 = fork();
          if (pid_com2 < 0) {
            perror("Il y a eu un problème durant le fork()");
            exit(1);
          }
          if (pid_com2 == 0) {
            if (dup2(pipefd[0], STDIN_FILENO) == -1) {
              fprintf(stderr, "probleme avec la redirection en entrée : %s", strerror(errno));
            }
            fprintf(stderr, "lancement de %s\n", arg_com2[0]);
            execvp(arg_com2[0], arg_com2);
            fprintf(stderr, "erreur avec la commande %s car : %s\n", arg_com2[0], strerror(errno));
            close(pipefd[0]);
            exit(2);
          } else {
            close(pipefd[0]);
          }
          // le père ne fait rien sinon attendre le dernier fils
        }

        if (attend) {
          attente_fils(pid_com2);
        }
      } else {
        fprintf(stderr, "Je ne sais pas faire ça\n");
        exit(1);
      }
     

      if (tab_com_pipe != NULL) {
        g_strfreev(tab_com_pipe);
      }
      if (arg_com1 != NULL) {
        g_strfreev(arg_com1);
      }
      if (arg_com2 != NULL) {
        g_strfreev(arg_com2);
      }
      if (text_com != NULL) {
        free(text_com);
      }
    }
   
   
  }  

  return 0;
}


